package com.easypan.service.impl;

import com.easypan.component.RedisComponent;
import com.easypan.entity.config.AppConfig;
import com.easypan.entity.constants.Constants;

import com.easypan.entity.dto.SessionWebUserDto;
import com.easypan.entity.dto.UploadResultDto;
import com.easypan.entity.dto.UserSpaceDto;
import com.easypan.entity.enums.*;

import com.easypan.entity.po.FileInfo;
import com.easypan.entity.po.FileShare;
import com.easypan.entity.po.UserInfo;
import com.easypan.entity.query.FileInfoQuery;
import com.easypan.entity.query.SimplePage;
import com.easypan.entity.query.UserInfoQuery;
import com.easypan.entity.vo.PaginationResultVO;
import com.easypan.exception.BusinessException;

import com.easypan.mappers.FileInfoMapper;
import com.easypan.mappers.FileShareMapper;
import com.easypan.mappers.UserInfoMapper;
import com.easypan.service.FileInfoService;
import com.easypan.service.FileShareService;
import com.easypan.utils.DateUtil;
import com.easypan.utils.ProcessUtils;
import com.easypan.utils.ScaleFilter;
import com.easypan.utils.StringTools;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import org.apache.commons.io.FileUtils;
import org.apache.ibatis.transaction.Transaction;
import org.apache.logging.log4j.util.ProcessIdUtil;
import org.aspectj.util.FileUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 文件信息表
 业务接口实现
 */
@Service("fileInfoService")
public class FileInfoServiceImpl implements FileInfoService {



	@Resource
	private FileInfoMapper<FileInfo, FileInfoQuery> fileInfoMapper;

	@Resource
	private RedisComponent redisComponent;

	@Resource
	private UserInfoMapper<UserInfo, UserInfoQuery> userInfoMapper;

	@Resource
	private AppConfig appConfig;

	private static final Logger logger = LoggerFactory.getLogger(FileInfoServiceImpl.class);


	@Resource
	@Lazy
	private FileInfoServiceImpl fileInfoService;
	private String userId;

	/**
	 * 根据条件查询列表
	 */
	@Override
	public List<FileInfo> findListByParam(FileInfoQuery param) {
		return this.fileInfoMapper.selectList(param);
	}

	/**
	 * 根据条件查询列表
	 */
	@Override
	public Integer findCountByParam(FileInfoQuery param) {
		return this.fileInfoMapper.selectCount(param);
	}

	/**
	 * 分页查询方法
	 */
	@Override
	public PaginationResultVO<FileInfo> findListByPage(FileInfoQuery param) {
		int count = this.findCountByParam(param);
		int pageSize = param.getPageSize() == null ? PageSize.SIZE15.getSize() : param.getPageSize();

		SimplePage page = new SimplePage(param.getPageNo(), count, pageSize);
		param.setSimplePage(page);
		List<FileInfo> list = this.findListByParam(param);
		PaginationResultVO<FileInfo> result = new PaginationResultVO(count, page.getPageSize(), page.getPageNo(), page.getPageTotal(), list);
		return result;
	}

	/**
	 * 新增
	 */
	@Override
	public Integer add(FileInfo bean) {
		return this.fileInfoMapper.insert(bean);
	}

	/**
	 * 批量新增
	 */
	@Override
	public Integer addBatch(List<FileInfo> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.fileInfoMapper.insertBatch(listBean);
	}

	/**
	 * 批量新增或者修改
	 */
	@Override
	public Integer addOrUpdateBatch(List<FileInfo> listBean) {
		if (listBean == null || listBean.isEmpty()) {
			return 0;
		}
		return this.fileInfoMapper.insertOrUpdateBatch(listBean);
	}

	/**
	 * 多条件更新
	 */
	@Override
	public Integer updateByParam(FileInfo bean, FileInfoQuery param) {
		StringTools.checkParam(param);
		return this.fileInfoMapper.updateByParam(bean, param);
	}

	/**
	 * 多条件删除
	 */
	@Override
	public Integer deleteByParam(FileInfoQuery param) {
		StringTools.checkParam(param);
		return this.fileInfoMapper.deleteByParam(param);
	}

	/**
	 * 根据FileIdAndUserId获取对象
	 */
	@Override
	public FileInfo getFileInfoByFileIdAndUserId(String fileId, String userId) {
		return this.fileInfoMapper.selectByFileIdAndUserId(fileId, userId);
	}

	/**
	 * 根据FileIdAndUserId修改
	 */
	@Override
	public Integer updateFileInfoByFileIdAndUserId(FileInfo bean, String fileId, String userId) {
		return this.fileInfoMapper.updateByFileIdAndUserId(bean, fileId, userId);
	}

	/**
	 * 根据FileIdAndUserId删除
	 */
	@Override
	public Integer deleteFileInfoByFileIdAndUserId(String fileId, String userId) {
		return this.fileInfoMapper.deleteByFileIdAndUserId(fileId, userId);
	}

	private String autoRename(String filePid, String userId, String fileName) {
		FileInfoQuery fileInfoQuery = new FileInfoQuery();
		fileInfoQuery.setUserId(userId);
		fileInfoQuery.setFilePid(filePid);
		fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
		fileInfoQuery.setFileName(fileName);
		Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
		if (count > 0) {
			return StringTools.rename(fileName);
		}

		return fileName;
	}

	private void updateUserSpace(SessionWebUserDto webUserDto,Long totalSize){
		Integer count=userInfoMapper.updateUserSpace(webUserDto.getUserId(),totalSize,null);
		if(count==0){
//			如果余下的是0，总空间就不足了
			throw new BusinessException(ResponseCodeEnum.CODE_904);
		}
//		拿到用户现在的空间
		UserSpaceDto spaceDto=redisComponent.getUserSpaceUse(webUserDto.getUserId());
//		设置一下现在使用了的空间
		spaceDto.setUseSpace(spaceDto.getUseSpace()+totalSize);
//		微服务里面添加上
		redisComponent.saveUserSpaceUse(webUserDto.getUserId(),spaceDto);
	}



	@Override
	@Transactional(rollbackFor = Exception.class)
	public UploadResultDto uploadFile(SessionWebUserDto webUserDto, String fileId, MultipartFile file, String fileName, String filePid, String fileMd5,
									  Integer chunkIndex, Integer chunks) {
		File tempFileFolder = null;
		Boolean uploadSuccess = true;
		try {
			UploadResultDto resultDto = new UploadResultDto();
			if (StringTools.isEmpty(fileId)) {//检测文件是否存在，因为分片上传，有可能是其他分片上传过来
				fileId = StringTools.getRandomString(Constants.LENGTH_10);
			}
			resultDto.setFileId(fileId);
			Date curDate = new Date();
			UserSpaceDto spaceDto = redisComponent.getUserSpaceUse(webUserDto.getUserId());//获取用户当前空间大小
			if (chunkIndex == 0) {
				FileInfoQuery infoQuery = new FileInfoQuery();
				infoQuery.setFileMd5(fileMd5);
				infoQuery.setSimplePage(new SimplePage(0, 1));
				infoQuery.setStatus(FileStatusEnums.USING.getStatus());
				List<FileInfo> dbFileList = this.fileInfoMapper.selectList(infoQuery);
				//秒传
				if (!dbFileList.isEmpty()) {
					System.out.println("这个是被秒传的文件");
					System.out.println(dbFileList);
					FileInfo dbFile = dbFileList.get(0);
					//判断文件状态，看看是否会超过总空间
					if (dbFile.getFileSize() + spaceDto.getUseSpace() > spaceDto.getTotalSpace()) {
						throw new BusinessException(ResponseCodeEnum.CODE_904);
					}
					dbFile.setFileId(fileId);
					dbFile.setFilePid(filePid);
					dbFile.setUserId(webUserDto.getUserId());
					dbFile.setFileMd5(null);
					dbFile.setCreateTime(curDate);
					dbFile.setLastUpdateTime(curDate);
					dbFile.setStatus(FileStatusEnums.USING.getStatus());
					dbFile.setDelFlag(FileDelFlagEnums.USING.getFlag());
					dbFile.setFileMd5(fileMd5);
					fileName = autoRename(filePid, webUserDto.getUserId(), fileName);
					dbFile.setFileName(fileName);
					this.fileInfoMapper.insert(dbFile);
					resultDto.setStatus(UploadStatusEnums.UPLOAD_SECONDS.getCode());
					//更新用户空间使用
					updateUserSpace(webUserDto, dbFile.getFileSize());

					return resultDto;
				}
			}
			//暂存在临时目录
			String tempFolderName = appConfig.getProjectFolder() + Constants.FILE_FOLDER_TEMP;
			String currentUserFolderName = webUserDto.getUserId() + fileId;
			//创建临时目录
			tempFileFolder = new File(tempFolderName + currentUserFolderName);
			if (!tempFileFolder.exists()) {
				tempFileFolder.mkdirs();
			}

			//判断磁盘空间
			Long currentTempSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);//将传上来的文件存在redis里，不用写一条sql去查
			if (file.getSize() + currentTempSize + spaceDto.getUseSpace() > spaceDto.getTotalSpace()) {
				throw new BusinessException(ResponseCodeEnum.CODE_904);
			}

			File newFile = new File(tempFileFolder.getPath() + "/" + chunkIndex);
			file.transferTo(newFile);
			//保存临时大小
			redisComponent.saveFileTempSize(webUserDto.getUserId(), fileId, file.getSize());
			//不是最后一个分片，直接返回
			if (chunkIndex < chunks - 1) {
				resultDto.setStatus(UploadStatusEnums.UPLOADING.getCode());
				return resultDto;
			}
			//最后一个分片上传完成，记录数据库，异步合并分片
			String month = DateUtil.format(curDate, DateTimePatternEnum.YYYYMM.getPattern());
			String fileSuffix = StringTools.getFileSuffix(fileName);
			//真实文件名
			String realFileName = currentUserFolderName + fileSuffix;
			FileTypeEnums fileTypeEnum = FileTypeEnums.getFileTypeBySuffix(fileSuffix);
			//自动重命名
			fileName = autoRename(filePid, webUserDto.getUserId(), fileName);
			FileInfo fileInfo = new FileInfo();
			fileInfo.setFileId(fileId);
			fileInfo.setUserId(webUserDto.getUserId());
			fileInfo.setFileMd5(fileMd5);
			fileInfo.setFileName(fileName);
			fileInfo.setFilePath(month + "/" + realFileName);
			fileInfo.setFilePid(filePid);
			fileInfo.setCreateTime(curDate);
			fileInfo.setLastUpdateTime(curDate);
			fileInfo.setFileCategory(fileTypeEnum.getCategory().getCategory());
			fileInfo.setFileType(fileTypeEnum.getType());
			fileInfo.setStatus(FileStatusEnums.TRANSFER.getStatus());
			fileInfo.setFolderType(FileFolderTypeEnums.FILE.getType());
			fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
			//最后一片文件上传结束后，插入数据库
			this.fileInfoMapper.insert(fileInfo);
			//因为redis存的是每一个小分片的大小，这样子拿拿到的是总的文件大小
			Long totalSize = redisComponent.getFileTempSize(webUserDto.getUserId(), fileId);
			updateUserSpace(webUserDto, totalSize);//更新

			resultDto.setStatus(UploadStatusEnums.UPLOAD_FINISH.getCode());//设置为转码中
			//事务提交后调用异步方法
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
				@Override
				public void afterCommit() {
					//这一步是转码，其实就是把文件信息给进去，里面会把分片的文件合成一份
					fileInfoService.transferFile(fileInfo.getFileId(), webUserDto);
				}
			});
			return resultDto;
		} catch (BusinessException e) {
			uploadSuccess = false;
			logger.error("文件上传失败", e);
			throw e;
		} catch (Exception e) {
			uploadSuccess = false;
			logger.error("文件上传失败", e);
			throw new BusinessException("文件上传失败");
		} finally {
			//如果上传失败，清除临时目录
			if (tempFileFolder != null && !uploadSuccess) {
				try {
					FileUtils.deleteDirectory(tempFileFolder);
				} catch (IOException e) {
					logger.error("删除临时目录失败");
				}
			}
		}
	}

	public static void union(String dirPath, String toFilePath, String fileName, boolean delSource) throws BusinessException {
		File dir = new File(dirPath);//这里是临时存放的目录
		if (!dir.exists()) {
			throw new BusinessException("目录不存在");
		}
		File fileList[] = dir.listFiles();//拿到这个目录下的所有文件目录
		File targetFile = new File(toFilePath);//目标生成目录
		RandomAccessFile writeFile = null;
		try {
			writeFile = new RandomAccessFile(targetFile, "rw");
			byte[] b = new byte[1024 * 10];//这里是设置一个缓冲区
			for (int i = 0; i < fileList.length; i++) {
				int len = -1;
				//创建读块文件的对象
				File chunkFile = new File(dirPath + File.separator + i);
				RandomAccessFile readFile = null;
				try {
					readFile = new RandomAccessFile(chunkFile, "r");
					while ((len = readFile.read(b)) != -1) {
						writeFile.write(b, 0, len);//读文件夹里面的分片
					}
				} catch (Exception e) {
					logger.error("合并分片失败", e);
					throw new BusinessException("合并文件失败");
				} finally {
					readFile.close();
				}
			}
		} catch (Exception e) {
			logger.error("合并文件:{}失败", fileName, e);
			throw new BusinessException("合并文件" + fileName + "出错了");
		} finally {
			try {
				if (null != writeFile) {
					writeFile.close();
				}
			} catch (IOException e) {
				logger.error("关闭流失败", e);
			}
			if (delSource) {
				if (dir.exists()) {
					try {
						FileUtils.deleteDirectory(dir);
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

	private void cutFile4Video(String fileId, String videoFilePath) {
//		将整个的文件切割为多个ts文件与一个m3u8_name后缀的文件，m3u8文件里面会存放着这个视频文件的主要信息，如播放时长，到时候前端拿就是一个ts一个ts拿
		//创建同名切片目录
		File tsFolder = new File(videoFilePath.substring(0, videoFilePath.lastIndexOf(".")));
		if (!tsFolder.exists()) {
			tsFolder.mkdirs();
		}
//		下面两个是ffmepg方法的
		final String CMD_TRANSFER_2TS = "ffmpeg -y -i %s  -vcodec copy -acodec copy -vbsf h264_mp4toannexb %s";
		final String CMD_CUT_TS = "ffmpeg -i %s -c copy -map 0 -f segment -segment_list %s -segment_time 30 %s/%s_%%4d.ts";

		String tsPath = tsFolder + "/" + Constants.TS_NAME;
		//生成.ts
		String cmd = String.format(CMD_TRANSFER_2TS, videoFilePath, tsPath);
		ProcessUtils.executeCommand(cmd, false);
		//生成索引文件.m3u8 和切片.ts
		cmd = String.format(CMD_CUT_TS, tsPath, tsFolder.getPath() + "/" + Constants.M3U8_NAME, tsFolder.getPath(), fileId);
		ProcessUtils.executeCommand(cmd, false);
		//删除index.ts
		new File(tsPath).delete();
	}

	//转码
	@Async
	public void transferFile(String fileId,SessionWebUserDto webUserDto){
		Boolean transferSuccess=true;//默认是转码成功的
		String targetFilePath=null;//目标生成的路径
		String cover=null;//缩略图
		FileTypeEnums fileTypeEnum=null;//文件类型，后续如果是视频文件要进行切割
		FileInfo fileInfo=fileInfoMapper.selectByFileIdAndUserId(fileId,webUserDto.getUserId());//查到文件信息
		try{
			//文件在数据库中不存在或者不处于转码状态，return掉
			if(fileInfo == null||!FileStatusEnums.TRANSFER.getStatus().equals(fileInfo.getStatus())){
				return;
			}

			//临时目录
			String tempFolderName=appConfig.getProjectFolder()+Constants.FILE_FOLDER_TEMP;
			String currentUserFolderName=webUserDto.getUserId()+fileId;
			File fileFolder=new File(tempFolderName+currentUserFolderName);//这个是现在分片上来每一片文件的目录名

			//创建
			if(!fileFolder.exists()){
				fileFolder.mkdirs();
			}

			//文件后缀
			String fileSuffix=StringTools.getFileSuffix(fileInfo.getFileName());
			String month=DateUtil.format(fileInfo.getCreateTime(),DateTimePatternEnum.YYYYMM.getPattern());

			//目标目录
			String targetFolderName =appConfig.getProjectFolder()+Constants.FILE_FOLDER_FILE;//最后存放的目录名
			File targetFolder=new File(targetFolderName+"/"+month);//最后存放的文件目录

			if(!targetFolder.exists()){
				targetFolder.mkdirs();
			}
			//真实文件名
			String realFileName=currentUserFolderName+fileSuffix;
			//真实文件路径
			targetFilePath=targetFolder.getPath()+"/"+realFileName;
			//合并文件,传临时目录，目标目录，文件名
			//分别是 临时目录（现存文件目录），目标文件目录，文件名，最后是否要删除临时目录
			union(fileFolder.getPath(),targetFilePath,fileInfo.getFileName(),true);

			//视频文件切割
			fileTypeEnum=FileTypeEnums.getFileTypeBySuffix(fileSuffix);
			if(FileTypeEnums.VIDEO==fileTypeEnum){//如果是视频文件要进行切割
				cutFile4Video(fileId,targetFilePath);//切割
				//视频生成缩略图
				cover=month+"/"+currentUserFolderName+Constants.IMAGE_PNG_SUFFIX;
				String coverPath=targetFolderName+"/"+cover;
				//调用scaleFilter生成视频缩略图
				ScaleFilter.createCover4Video(new File(targetFilePath),Constants.LENGTH_150,new File(coverPath));
			}else if(FileTypeEnums.IMAGE==fileTypeEnum){
				//生成缩略图
				cover=month+"/"+realFileName.replace(".","_.");//一张是标准图，一张是缩略图
				String coverPath=targetFolderName+"/"+cover;
				Boolean created =ScaleFilter.createThumbnailWidthFFmpeg
						(new File(targetFilePath),Constants.LENGTH_150,new File(coverPath),false);
				if(!created){
					FileUtils.copyFile(new File(targetFilePath),new File(coverPath));

				}

			}


		}catch (Exception e){
			logger.error("文件转码失败，文件Id:{}",fileId,webUserDto.getUserId(),e);
			transferSuccess=false;
		}finally {
			FileInfo updateInfo=new FileInfo();
			updateInfo.setFileSize(new File(targetFilePath).length());

			updateInfo.setFileCover(cover);
			updateInfo.setStatus(transferSuccess ? FileStatusEnums.USING.getStatus() : FileStatusEnums.TRANSFER_FAIL.getStatus());
			fileInfoMapper.updateFileStatusWithOldStatus(fileId,webUserDto.getUserId(),updateInfo,FileStatusEnums.TRANSFER.getStatus());
		}

	}

//	public FileInfo newFolder(String filePid,String userId,String folderName){
//		checkFileName(filePid,userId,folderName,FileFolderTypeEnums.FOLDER.getType());//检查文件是否重复
//        Date curDate=new Date();
//        FileInfo fileInfo=new FileInfo();
//        fileInfo.setFileId(StringTools.getRandomString(Constants.LENGTH_10));
//        fileInfo.setFilePid(filePid);
//        fileInfo.setFileName(folderName);
//        fileInfo.setFolderType(FileFolderTypeEnums.FOLDER.getType());
//        fileInfo.setCreateTime(curDate);
//        fileInfo.setLastUpdateTime(curDate);
//		fileInfo.setUserId(userId);
//        fileInfo.setStatus(FileStatusEnums.USING.getStatus());
//        fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
//        this.fileInfoMapper.insert(fileInfo);
//
//		FileInfoQuery fileInfoQuery = new FileInfoQuery();
//		fileInfoQuery.setFilePid(filePid);
//		fileInfoQuery.setUserId(userId);
//		fileInfoQuery.setFileName(folderName);
//		fileInfoQuery.setFolderType(FileFolderTypeEnums.FOLDER.getType());
//		fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
//		Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
//		if (count > 1) {
//			throw new BusinessException("文件夹" + folderName + "已经存在");
//		}
//		fileInfo.setFileName(folderName);
//		fileInfo.setLastUpdateTime(curDate);
//		return fileInfo;
//
//
//
//
//	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public FileInfo newFolder(String filePid, String userId, String folderName) {
		checkFileName(filePid, userId, folderName, FileFolderTypeEnums.FOLDER.getType());
		Date curDate = new Date();
		FileInfo fileInfo = new FileInfo();
		fileInfo.setFileId(StringTools.getRandomString(Constants.LENGTH_10));
		fileInfo.setUserId(userId);
		fileInfo.setFilePid(filePid);
		fileInfo.setFileName(folderName);
		fileInfo.setFolderType(FileFolderTypeEnums.FOLDER.getType());
		fileInfo.setCreateTime(curDate);
		fileInfo.setLastUpdateTime(curDate);
		fileInfo.setStatus(FileStatusEnums.USING.getStatus());
		fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
		this.fileInfoMapper.insert(fileInfo);

		FileInfoQuery fileInfoQuery = new FileInfoQuery();
		fileInfoQuery.setFilePid(filePid);
		fileInfoQuery.setUserId(userId);
		fileInfoQuery.setFileName(folderName);
		fileInfoQuery.setFolderType(FileFolderTypeEnums.FOLDER.getType());
		fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
		Integer count = this.fileInfoMapper.selectCount(fileInfoQuery);
		if (count > 1) {
			throw new BusinessException("文件夹" + folderName + "已经存在");
		}
		fileInfo.setFileName(folderName);
		fileInfo.setLastUpdateTime(curDate);
		return fileInfo;
	}

	public void checkFileName(String filePid,String userId,String fileName,Integer folderType){
		FileInfoQuery fileInfoQuery=new FileInfoQuery();
		fileInfoQuery.setFolderType(folderType);
		fileInfoQuery.setFilePid(filePid);
		fileInfoQuery.setFileName(fileName);
		fileInfoQuery.setUserId(userId);
		fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
		Integer count=this.fileInfoMapper.selectCount(fileInfoQuery);
		if(count>0){
			throw new BusinessException("此目录下已存在同名文件,请修改名称");
		}

	}

	@Override
	public FileInfo rename(String fileId, String userId, String fileName) {
		FileInfo fileInfo=this.fileInfoMapper.selectByFileIdAndUserId(fileId, userId);
		if(fileInfo==null){
			throw new BusinessException("文件不存在");
		}
		if(fileInfo.getFileName().equals(fileName)){
			return fileInfo;//同名的条件下不用管了，直接返回就可以
		}
		String filePid=fileInfo.getFilePid();
		checkFileName(filePid, userId,fileName,fileInfo.getFolderType());//check一下是否是否会同名，同名会有提示的
		//文件获取后缀，因为他是只改名，后缀是不能改的，所以前端传过来的这个是改名后，没有后缀的字符串，需要我自己拼起来
		if(FileFolderTypeEnums.FILE.getType().equals(fileInfo.getFolderType())){
			fileName=fileName+StringTools.getFileSuffix(fileInfo.getFileName());
		}
		Date curDate=new Date();
		FileInfo dbInfo=new FileInfo();//new一个数据库对象
		dbInfo.setFileName(fileName);
		dbInfo.setLastUpdateTime(curDate);
		this.fileInfoMapper.updateByFileIdAndUserId(dbInfo,fileId, userId);

		//事件回滚
		FileInfoQuery fileInfoQuery=new FileInfoQuery();
		fileInfoQuery.setFilePid(filePid);
		fileInfoQuery.setUserId(userId);
		fileInfoQuery.setFileName(fileName);
		fileInfoQuery.setDelFlag(FileDelFlagEnums.USING.getFlag());
		Integer count=this.fileInfoMapper.selectCount(fileInfoQuery);
		if(count>1){
			throw new BusinessException("文件名"+fileName+"已经存在");
		}
		fileInfo.setFileName(fileName);
		fileInfo.setLastUpdateTime(curDate);
		return fileInfo;
	}

	@Transactional(rollbackFor = Exception.class)
	public void changeFileFolder(String fileIds, String filePid, String userId) {
		if (fileIds.equals(filePid)) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		if (!Constants.ZERO_STR.equals(filePid)) {
			FileInfo fileInfo = fileInfoService.getFileInfoByFileIdAndUserId(filePid, userId);
			if (fileInfo == null || !FileDelFlagEnums.USING.getFlag().equals(fileInfo.getDelFlag())) {
				throw new BusinessException(ResponseCodeEnum.CODE_600);
			}
		}
		String[] fileIdArray = fileIds.split(",");

		FileInfoQuery query = new FileInfoQuery();
		query.setFilePid(filePid);
		query.setUserId(userId);
		List<FileInfo> dbFileList = fileInfoService.findListByParam(query);

		Map<String, FileInfo> dbFileNameMap = dbFileList.stream().collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (file1, file2) -> file2));
		//查询选中的文件
		query = new FileInfoQuery();
		query.setUserId(userId);
		query.setFileIdArray(fileIdArray);
		List<FileInfo> selectFileList = fileInfoService.findListByParam(query);

		//将所选文件重命名
		for (FileInfo item : selectFileList) {
			FileInfo rootFileInfo = dbFileNameMap.get(item.getFileName());
			//文件名已经存在，重命名被还原的文件名
			FileInfo updateInfo = new FileInfo();
			if (rootFileInfo != null) {
				String fileName = StringTools.rename(item.getFileName());
				updateInfo.setFileName(fileName);
			}
			updateInfo.setFilePid(filePid);
			this.fileInfoMapper.updateByFileIdAndUserId(updateInfo, item.getFileId(), userId);
		}



	}

	@Transactional(rollbackFor = Exception.class)
	public void removeFile2RecycleBatch(String userId,String fileIds){
		//转过来的fileIds
		String[] fileIdArray=fileIds.split(",");
		FileInfoQuery query =new FileInfoQuery();
		query.setDelFlag(FileDelFlagEnums.USING.getFlag());
		query.setFileIdArray(fileIdArray);
		query.setUserId(userId);
		List<FileInfo> fileIdList=this.fileInfoMapper.selectList(query);
		if(fileIdList.isEmpty()){
			return;
		}

		List<String> delFilePidList=new ArrayList<>();
		for(FileInfo fileInfo:fileIdList){
			findAllSubFolderFileIdList(delFilePidList, userId,fileInfo.getFileId(),FileDelFlagEnums.USING.getFlag());
		}

		//经过递归后，到这里的delFilePidList
//		将目录删除，文件进回收站
		if(!delFilePidList.isEmpty()){
			FileInfo updateInfo =new FileInfo();
			updateInfo.setDelFlag(FileDelFlagEnums.DEL.getFlag());
			this.fileInfoMapper.updateFileDelFlagBatch(updateInfo, userId,delFilePidList,null,FileDelFlagEnums.DEL.getFlag());
		}
		//将所有文件更新到回收站
		List<String> delFileIdList=Arrays.asList(fileIdArray);
		FileInfo recycleInfo=new FileInfo();
		recycleInfo.setRecoveryTime(new Date());
		recycleInfo.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
		this.fileInfoMapper.updateFileDelFlagBatch(recycleInfo, userId,null,delFileIdList,FileDelFlagEnums.USING.getFlag());

	}

	private void findAllSubFolderFileIdList(List <String> fileIdList ,String userId,String fileId,Integer delFlag){
		//这个方法其实是为了把filePid也给进去，就是有可能里面是有目录的那种的文件夹的
		fileIdList.add(fileId);
		FileInfoQuery query =new FileInfoQuery();
		query.setUserId(userId);
		query.setDelFlag(FileDelFlagEnums.USING.getFlag());
		query.setFolderType(FileFolderTypeEnums.FOLDER.getType());
		query.setFilePid(fileId);
		List<FileInfo> fileInfoList=this.fileInfoMapper.selectList(query);
		for(FileInfo fileInfo:fileInfoList){
			findAllSubFolderFileIdList(fileIdList, userId,fileInfo.getFileId(),delFlag);
		}


	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void recoverFileBatch(String userId, String fileIds){
		String[] fileIdArray=fileIds.split(",");
		FileInfoQuery query=new FileInfoQuery();
		query.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
		query.setUserId(userId);
		query.setFileIdArray(fileIdArray);
		List<FileInfo> fileInfoList=this.fileInfoMapper.selectList(query);

		List <String>delFileFolderList=new ArrayList<>();
		for(FileInfo fileInfo:fileInfoList){
			if(FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())){
				this.userId = userId;
				findAllSubFolderFileIdList(delFileFolderList,userId,fileInfo.getFileId(),FileDelFlagEnums.RECYCLE.getFlag());
			}
		}

		//查询所有根目录文件
		query=new FileInfoQuery();
		query.setUserId(userId);
		query.setDelFlag(FileDelFlagEnums.USING.getFlag());
		query.setFilePid(Constants.ZERO_STR);
		List<FileInfo> allRootFileList = this.fileInfoMapper.selectList(query);
		Map<String,FileInfo>rootFileMap=allRootFileList.stream().collect(Collectors.toMap(FileInfo::getFileName,Function.identity(),(file1,file2)->file2));

		//将目录下所有的目录更新为正常
		if(!delFileFolderList.isEmpty()){
			FileInfo fileInfo=new FileInfo();
			fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
			fileInfo.setUserId(userId);
			this.fileInfoMapper.updateFileDelFlagBatch(fileInfo,userId,delFileFolderList,null,FileDelFlagEnums.DEL.getFlag());
		}
		//将所选中的文件更新为正常
		List<String> updatefileList=Arrays.asList(fileIdArray);
		FileInfo fileInfo =new FileInfo();
		fileInfo.setUserId(userId);
		fileInfo.setLastUpdateTime(new Date());
		fileInfo.setDelFlag(FileDelFlagEnums.USING.getFlag());
		this.fileInfoMapper.updateFileDelFlagBatch(fileInfo,userId,null,updatefileList,FileDelFlagEnums.RECYCLE.getFlag());

		//将所选文件重命名
		for(FileInfo item:fileInfoList){
			FileInfo updateFileInfo=rootFileMap.get(item.getFileName());
			if(updateFileInfo!=null){
				String fileName=StringTools.rename(item.getFileName());
				FileInfo updateInfo=new FileInfo();
				updateInfo.setFileName(fileName);
				this.fileInfoMapper.updateByFileIdAndUserId(updateInfo,item.getFileId(),userId);
			}
		}
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public void delFileBatch(String userId,String fileIds,Boolean adminOp){
		String [] fileIdArray=fileIds.split(",");
		FileInfoQuery query=new FileInfoQuery();
		query.setUserId(userId);
		query.setFileIdArray(fileIdArray);
		if(!adminOp){
			query.setDelFlag(FileDelFlagEnums.RECYCLE.getFlag());
		}
		//查出要删除的文件id的List
		List<FileInfo> fileInfoList=this.fileInfoMapper.selectList(query);
		List <String> delFileFolderList=new ArrayList<>();
		for(FileInfo fileInfo:fileInfoList){
			if(FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())){
				findAllSubFolderFileIdList(delFileFolderList,userId,fileInfo.getFileId(),FileDelFlagEnums.DEL.getFlag());
			}
		}

		if(!delFileFolderList.isEmpty()){
			this.fileInfoMapper.delFileBatch(userId,delFileFolderList,null,FileDelFlagEnums.DEL.getFlag());
		}

		//删除所选文件
		List<String> fileIdDelList=Arrays.asList(fileIdArray);
		this.fileInfoMapper.delFileBatch(userId,null,fileIdDelList,FileDelFlagEnums.RECYCLE.getFlag());

		//重设用户存储空间
		Long userSpace=this.fileInfoMapper.selectUseSpace(userId);
		UserInfo userInfo=new UserInfo();
		userInfo.setUseSpace(userSpace);
		this.userInfoMapper.updateByUserId(userInfo,userId);

		//设置缓存
		UserSpaceDto userSpaceDto=redisComponent.getUserSpaceUse(userId);
		userSpaceDto.setUseSpace(userSpace);
		redisComponent.saveUserSpaceUse(userId,userSpaceDto);
	}

	@Override
	public void checkFilePid(String rootFilePid,String userId,String filePid){
		//rootFilePid是查出来应该有的pid，filePid是前端给的
		if(StringTools.isEmpty(rootFilePid)){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		if(rootFilePid.equals(filePid)){
			return;
		}
		this.checkRootPid(rootFilePid,userId,filePid);

	}

	private void checkRootPid(String rootFilePid,String userId,String filePid){
		FileInfo fileInfo=this.fileInfoService.getFileInfoByFileIdAndUserId(filePid,userId);
		if(fileInfo==null){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		if(Constants.ZERO_STR.equals(fileInfo.getFilePid())){
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}

		if(fileInfo.getFilePid().equals(rootFilePid)){
			return;
		}
		checkRootPid(rootFilePid,userId,fileInfo.getFilePid());

	}

	@Override
	public void deleteFileByUserId(String userId) {
		this.fileInfoMapper.deleteFileByUserId(userId);
	}



	@Override
	public void checkRootFilePid(String rootFilePid, String userId, String fileId) {
		if (StringTools.isEmpty(fileId)) {
			throw new BusinessException(ResponseCodeEnum.CODE_600);
		}
		if (rootFilePid.equals(fileId)) {
			return;
		}
		checkFilePid(rootFilePid, fileId, userId);
	}
	@Override
	public Long getUserUseSpace(String userId) {
		return this.fileInfoMapper.selectUseSpace(userId);
	}

	@Override
	@Transactional
	public void saveShare(String shareRootFilePid, String shareFileIds, String myFolderId, String shareUserId, String cureentUserId) {
		String[] shareFileIdArray = shareFileIds.split(",");
		//目标目录文件列表
		FileInfoQuery fileInfoQuery = new FileInfoQuery();
		fileInfoQuery.setUserId(cureentUserId);
		fileInfoQuery.setFilePid(myFolderId);
		List<FileInfo> currentFileList = this.fileInfoMapper.selectList(fileInfoQuery);
		Map<String, FileInfo> currentFileMap = currentFileList.stream()
				.collect(Collectors.toMap(FileInfo::getFileName, Function.identity(), (file1, file2) -> file2));
		//选择的文件
		fileInfoQuery = new FileInfoQuery();
		fileInfoQuery.setUserId(shareUserId);
		fileInfoQuery.setFileIdArray(shareFileIdArray);
		List<FileInfo> shareFileList = this.fileInfoMapper.selectList(fileInfoQuery);
		//重命名选择的文件
		List<FileInfo> copyFileList = new ArrayList<>();
		Date curDate = new Date();
		for (FileInfo item : shareFileList) {
			FileInfo haveFile = currentFileMap.get(item.getFileName());
			if (haveFile != null) {
				item.setFileName(StringTools.rename(item.getFileName()));
			}
			findAllSubFile(copyFileList, item, shareUserId, cureentUserId, curDate, myFolderId);
		}
		this.fileInfoMapper.insertBatch(copyFileList);
		//更新空间
		Long useSpace = this.fileInfoMapper.selectUseSpace(cureentUserId);
		UserInfo dbUserInfo = this.userInfoMapper.selectByUserId(cureentUserId);
		if (useSpace > dbUserInfo.getTotalSpace()) {
			throw new BusinessException(ResponseCodeEnum.CODE_904);
		}
		UserInfo userInfo = new UserInfo();
		userInfo.setUseSpace(useSpace);
		this.userInfoMapper.updateByUserId(userInfo, cureentUserId);
		//设置缓存
		UserSpaceDto userSpaceDto = redisComponent.getUserSpaceUse(cureentUserId);
		userSpaceDto.setUseSpace(useSpace);
		redisComponent.saveUserSpaceUse(cureentUserId, userSpaceDto);
	}


	private void findAllSubFile(List<FileInfo> copyFileList, FileInfo fileInfo, String sourceUserId, String currentUserId, Date curDate, String newFilePid) {
		String sourceFileId = fileInfo.getFileId();
		fileInfo.setCreateTime(curDate);
		fileInfo.setLastUpdateTime(curDate);
		fileInfo.setFilePid(newFilePid);
		fileInfo.setUserId(currentUserId);
		String newFileId = StringTools.getRandomString(Constants.LENGTH_10);
		fileInfo.setFileId(newFileId);
		copyFileList.add(fileInfo);
		if (FileFolderTypeEnums.FOLDER.getType().equals(fileInfo.getFolderType())) {
			FileInfoQuery query = new FileInfoQuery();
			query.setFilePid(sourceFileId);
			query.setUserId(sourceUserId);
			List<FileInfo> sourceFileList = this.fileInfoMapper.selectList(query);
			for (FileInfo item : sourceFileList) {
				findAllSubFile(copyFileList, item, sourceUserId, currentUserId, curDate, newFileId);
			}
		}
	}


}